package zheng.es;

import com.alibaba.fastjson.JSON;
//import com.hose.mall.util.mapper.JsonUtils;
//import com.hose.mall.util.string.StringUtils;
import lombok.extern.slf4j.Slf4j;
import org.elasticsearch.action.DocWriteResponse;
import org.elasticsearch.action.bulk.BulkItemResponse;
import org.elasticsearch.action.bulk.BulkRequest;
import org.elasticsearch.action.bulk.BulkResponse;
import org.elasticsearch.action.delete.DeleteRequest;
import org.elasticsearch.action.delete.DeleteResponse;
import org.elasticsearch.action.get.GetRequest;
import org.elasticsearch.action.get.GetResponse;
import org.elasticsearch.action.index.IndexRequest;
import org.elasticsearch.action.index.IndexResponse;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.action.search.SearchType;
import org.elasticsearch.action.update.UpdateRequest;
import org.elasticsearch.action.update.UpdateResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.common.unit.TimeValue;
import org.elasticsearch.common.xcontent.XContentType;
import org.elasticsearch.index.query.AbstractQueryBuilder;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.RangeQueryBuilder;
import org.elasticsearch.rest.RestStatus;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.SearchHits;
import org.elasticsearch.search.aggregations.Aggregation;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.search.fetch.subphase.FetchSourceContext;
import org.elasticsearch.search.sort.SortOrder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import java.io.IOException;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
import java.util.stream.Stream;

@Service("documentBasicsHandle")
@Slf4j
public class DocumentBasicsHandle {
    @Autowired
    RestHighLevelClient restHighLevelClient;

//    private static final JsonUtils jsonUtils = JsonUtils.allMapper();
//    private static final JsonUtils jsonUtils = JsonUtils.allMapper();

    public boolean existsDocument(String indexName, String id) {
        boolean isSuccess = false;
        try {
            GetRequest getRequest = new GetRequest(indexName, id);
            getRequest.fetchSourceContext(new FetchSourceContext(false));
            isSuccess = restHighLevelClient.exists(getRequest, RequestOptions.DEFAULT);
        } catch (IOException e) {
            log.error("existsDocument fail", e);
        }
        return isSuccess;
    }


    public boolean addDocument(String indexName, String id, Map<String, Object> dataMap) {
        boolean isSuccess = false;
        try {
            IndexRequest request = new IndexRequest(indexName);
            request.id(id);
            request.source(dataMap, XContentType.JSON);
            IndexResponse indexResponse = restHighLevelClient.index(request, RequestOptions.DEFAULT);
            if (indexResponse.getResult() == DocWriteResponse.Result.CREATED) {
                isSuccess = true;
            }
        } catch (IOException e) {
            log.error("addDocument fail", e);
        }
        return isSuccess;
    }


    public boolean deleteDocument(String indexName, String id) {
        boolean isSuccess = false;
        try {
            DeleteRequest request = new DeleteRequest(indexName, id);
            DeleteResponse deleteResponse = restHighLevelClient.delete(request, RequestOptions.DEFAULT);
            if (deleteResponse.getResult() == DocWriteResponse.Result.DELETED) {
                isSuccess = true;
            }
        } catch (IOException e) {
            log.error("deleteDocument fail", e);
        }
        return isSuccess;
    }


    public boolean updateDocument(String indexName, String id, Map<String, String> dataMap) {
        boolean isSuccess = false;
        try {
            UpdateRequest request = new UpdateRequest(indexName, id);
            request.doc(dataMap, XContentType.JSON);
            UpdateResponse updateResponse = restHighLevelClient.update(request, RequestOptions.DEFAULT);
            if (updateResponse.getResult() == DocWriteResponse.Result.UPDATED) {
                isSuccess = true;
            }
        } catch (IOException e) {
            log.error("updateDocument fail", e);
        }
        return isSuccess;
    }


    public String queryDocument(String indexName, String id) {
        String doc = null;
        try {
            GetRequest request = new GetRequest(indexName, id);
            GetResponse response = restHighLevelClient.get(request, RequestOptions.DEFAULT);
            doc = response.getSourceAsString();
        } catch (IOException e) {
            log.error("findDocument fail", e);
        }
        return doc;
    }


    public boolean batchSave(String indexName, Map<Long, Object> dataMap) {
        boolean isSuccess = false;
        if (dataMap == null || dataMap.isEmpty()) {
            return isSuccess;
        }
        try {
            long now = System.currentTimeMillis();
            BulkRequest request = new BulkRequest();
            for (Map.Entry<Long, Object> entry : dataMap.entrySet()) {
//                IndexRequest index = new IndexRequest(indexName).source(jsonUtils.toJson(entry.getValue()), XContentType.JSON);
                IndexRequest index = new IndexRequest(indexName).source(JSON.toJSONString(entry.getValue()), XContentType.JSON);
                index.id(String.valueOf(entry.getKey()));
                request.add(index);
            }
            BulkResponse bulkResponse = restHighLevelClient.bulk(request, RequestOptions.DEFAULT);
            if (bulkResponse.hasFailures()) {
                log.error("batchSave index name:{}  pageSie:{}  failed:{} complate time:{}", indexName, dataMap.size(), bulkResponse.buildFailureMessage(), (System.currentTimeMillis() - now) + "");
            } else {
                if (log.isDebugEnabled()) {
                    log.debug("batchSave index name:{} pageSie:{} success complate time:{}", indexName, dataMap.size(), (System.currentTimeMillis() - now) + "");
                }
                isSuccess = true;
            }
        } catch (IOException e) {
            log.error("batchSave fail", e);
        }
        return isSuccess;
    }

    public Boolean batchDelete(String indexName, List<Long> dataList) {
        boolean isSuccess = false;
        if (CollectionUtils.isEmpty(dataList)) {
            return isSuccess;
        }
        try {
            BulkRequest request = new BulkRequest();
            for (long id : dataList) {
                request.add(new DeleteRequest(indexName, String.valueOf(id)));
            }
            BulkResponse bulkResponse = restHighLevelClient.bulk(request, RequestOptions.DEFAULT);
            if (bulkResponse.hasFailures()) {
                for (BulkItemResponse item : bulkResponse.getItems()) {
                    log.error("batchDelete fail:{}", item.getFailureMessage());
                }
            } else {
                isSuccess = true;
            }
        } catch (IOException e) {
            log.error("batchDelete fail", e);
        }
        return isSuccess;
    }

    public Boolean batchUpdate(String indexName, Map<Long, Object> dataMap) {
        boolean isSuccess = false;
        if (dataMap == null || dataMap.isEmpty()) {
            return isSuccess;
        }
        BulkRequest request = new BulkRequest();
        for (Map.Entry<Long, Object> entry : dataMap.entrySet()) {
            UpdateRequest updateRequest =   new UpdateRequest(indexName, String.valueOf(entry.getKey()));
//            Map<String,Object> map = jsonUtils.fromJson(jsonUtils.toJson(entry.getValue()),HashMap.class);
            HashMap map = JSON.parseObject(JSON.toJSONString(entry.getValue()), HashMap.class);
            updateRequest.doc(map);
            request.add(updateRequest);
        }
        try {
            BulkResponse bulkResponse = restHighLevelClient.bulk(request, RequestOptions.DEFAULT);
            if (bulkResponse.hasFailures()) {
                for (BulkItemResponse item : bulkResponse.getItems()) {
                    log.error("batchUpdate fail:{}", item.getFailureMessage());
                }
            } else {
                isSuccess = true;
            }
        } catch (IOException e) {
            log.error("batchUpdate fail", e);
        }
        return isSuccess;
    }

    public BatchQueryResult batchQuery(BatchQueryParam batchQueryParam) {

        List<String> dataJsonList = new ArrayList<>();
        BatchQueryResult batchQueryResult = new BatchQueryResult();
        batchQueryResult.setCount(0);
        batchQueryResult.setDataJsonList(dataJsonList);

        List<List<Object>> sortValuesList = new ArrayList<>();
        try {
            SearchRequest searchRequest = buildRequest(batchQueryParam);
            SearchResponse response = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
            boolean rs = response != null && RestStatus.OK.equals(response.status());
            if (rs) {
                if(response.getHits().getTotalHits().value > 0){
                    batchQueryResult.setCount(Long.valueOf(response.getHits().getTotalHits().value).intValue());
                    SearchHits hits = response.getHits();

                    for (SearchHit hit : hits.getHits()) {
                        dataJsonList.add(hit.getSourceAsString());
                        sortValuesList.add(Stream.of(hit.getSortValues()).collect(Collectors.toList()));
                    }
                    //只保留最后一个排序字段
                    List<Object> sortValue = sortValuesList.stream().reduce((first, second) -> second).orElse(null);
                    batchQueryResult.setSortValue(sortValue);
                }

                if (Objects.nonNull(batchQueryParam.getAggregation())) {
                    Map<String, Aggregation> aggregationMap = response.getAggregations().asMap();
                    batchQueryResult.setAggregationMap(aggregationMap);
                }
            }
        } catch (IOException e) {
            log.error("batchQuery fail", e);
        }
        return batchQueryResult;
    }

    public SearchRequest buildRequest(BatchQueryParam batchQueryParam) {

        BoolQueryBuilder bool = new BoolQueryBuilder();
        if (!CollectionUtils.isEmpty(batchQueryParam.getAbstractQueryBuilderList())) {
            for (AbstractQueryBuilder abstractQueryBuilder : batchQueryParam.getAbstractQueryBuilderList()) {
                bool.filter(abstractQueryBuilder);
            }
        }

        if (!CollectionUtils.isEmpty(batchQueryParam.getRangeQueryBuilderList())) {
            for (RangeQueryBuilder rangeQueryBuilder : batchQueryParam.getRangeQueryBuilderList()) {
                bool.filter(rangeQueryBuilder);
            }
        }
        if (!CollectionUtils.isEmpty(batchQueryParam.getShouldClauseGroups())) {
            batchQueryParam.getShouldClauseGroups()
                    .forEach(shouldClauses -> {
                        BoolQueryBuilder boolForShould = new BoolQueryBuilder();
                        shouldClauses.forEach(boolForShould::should);
                        bool.filter(boolForShould);
                    });
        }

        SearchSourceBuilder searchSourceBuilder =
                buildAdapterSearchSource(batchQueryParam.getIncludes(), batchQueryParam.getExcludes());

        if (Objects.nonNull(batchQueryParam.getSize())) {
            searchSourceBuilder.size(batchQueryParam.getSize());
        }
        if (Objects.nonNull(batchQueryParam.getFrom())) {
            searchSourceBuilder.from(batchQueryParam.getFrom());
        }
        if (!StringUtils.isEmpty(batchQueryParam.getFieldSort())) {
            searchSourceBuilder.sort(batchQueryParam.getFieldSort(), SortOrder.fromString(batchQueryParam.getSortMethod()));
            if(!StringUtils.isEmpty(batchQueryParam.getUniqueColumnName())){
                searchSourceBuilder.sort(batchQueryParam.getUniqueColumnName(),SortOrder.fromString(batchQueryParam.getSortMethod()));
            }
        }
        if (!CollectionUtils.isEmpty(batchQueryParam.getSearchAfterList())) {
            searchSourceBuilder.searchAfter(batchQueryParam.getSearchAfterList().toArray());
        }

        searchSourceBuilder.query(bool);

        if (!Objects.isNull(batchQueryParam.getAggregation())) {
            searchSourceBuilder.aggregation(batchQueryParam.getAggregation());
        }

        log.info("buildRequest->dsl:" + searchSourceBuilder);

        SearchRequest searchRequest = new SearchRequest(batchQueryParam.getIndexName());
        searchSourceBuilder.trackTotalHits(true);
        searchRequest.source(searchSourceBuilder);
        searchRequest.searchType(SearchType.QUERY_THEN_FETCH);
        return searchRequest;
    }

    private static SearchSourceBuilder buildAdapterSearchSource(String[] includes, String[] excludes) {
        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
        searchSourceBuilder.explain(Boolean.FALSE);
        searchSourceBuilder.timeout(new TimeValue(6, TimeUnit.SECONDS));
        searchSourceBuilder.fetchSource(includes, excludes);
        return searchSourceBuilder;
    }
}
